<cfcomponent displayname="TicketService" extends="actionpacks.projectshen.model.AbstractBean" output="false">

	<cffunction name="setPersistenceService" returntype="void" access="public" output="false" hint="Import: Persistence Service">
		<cfargument name="persistenceService" required="true"/>
		<cfset variables.persistenceService = arguments.persistenceService />
	</cffunction>

	<cffunction name="getPersistenceService" returntype="any" access="private" output="false" hint="Returns Internal: Persistence Service">
		<cfreturn variables.persistenceService />
	</cffunction>

	<cffunction name="setEmailService" returntype="void" access="public" output="false" hint="Import: Persistence Service">
		<cfargument name="emailService" required="true"/>
		<cfset variables.emailService = arguments.emailService />
	</cffunction>

	<cffunction name="getEmailService" returntype="any" access="private" output="false" hint="Returns Internal: Persistence Service">
		<cfreturn variables.emailService />
	</cffunction>

	<cffunction name="setBeanInjector" returntype="void" access="public" output="false" hint="Import: Persistence Service">
		<cfargument name="beanInjector" required="true"/>
		<cfset variables.beanInjector = arguments.beanInjector />
	</cffunction>

	<cffunction name="getBeanInjector" returntype="any" access="private" output="false" hint="Returns Internal: Persistence Service">
		<cfreturn variables.beanInjector />
	</cffunction>

	<cffunction name="setG11nService" returntype="void" access="public" output="false" hint="coldspring:: Ticket Service">
		<cfargument name="G11nService" required="true"/>
		<cfset variables.G11nService = arguments.G11nService />
	</cffunction>

	<cffunction name="getG11nService" returntype="any" access="private" output="false" hint="internal: Project Service">
		<cfreturn variables.G11nService />
	</cffunction>

	<cffunction name="init" returntype="any" output="false" hint="Constructor">
		<cfargument name="G11nService" required="true"/>
		<cfset variables.conf = arguments.G11nService.getRB("persistence").getResourceBundleData()>		
		<cfreturn this />
	</cffunction>

	<cffunction name="createTicketObject" returntype="any" output="false" hint="Create Project Object">
		<cfargument name="componentName" default="actionpacks.projectshen.model.project.ticket.ProjectTicket">
		<cfset var ticketObject = createObject("component",arguments.componentName)>
		<cfreturn ticketObject />
	</cffunction>

	<cffunction name="runObjectQuery" access="private" returntype="any" output="false" roles="" hint="Returns: query">
		<cfargument name="objectname" default="" />
		<cfargument name="params" default="#structNew()#" />
		<cfset var conf = variables.conf />
		<cfset var ormObjectName = conf[arguments.objectname] />
		<cfset var query = getPersistenceService().query(ormObjectName,params)>
	  <cfreturn query />	
	</cffunction>

	<cffunction name="getObjectByID" access="private" returntype="any" output="false" roles="" hint="Returns: query">
		<cfargument name="objectname" required="true"  />
		<cfargument name="primarykey" required="true"  />
		<cfargument name="id" required="true" />
		<cfset var priKeys = structNew() />
		<cfset var conf = variables.conf />
		<cfset var ormObjectKey = conf[arguments.primarykey] />
		<cfset var ormObjectName = conf[arguments.objectname] />
		<cfset var ormObject = "" />
		<cfset priKeys[ormObjectKey] = arguments.id />
		<cfset ormObject = getPersistenceService().load(ormObjectName,priKeys)>
	  <cfreturn ormObject />	
	</cffunction>

	<cffunction name="getObject" access="private" returntype="any" output="false" roles="" hint="Returns: query">
		<cfargument name="objectname" default="" />
		<cfargument name="params" default="#structNew()#" />
		<cfset var conf = variables.conf />
		<cfset var ormObjectName = conf[arguments.objectname] />
		<cfset var ormobject = conf[arguments.objectname] />
		<cfset ormobject = getPersistenceService().load(ormObjectName,arguments.params)>
	  <cfreturn ormobject />	
	</cffunction>

	<cffunction name="getTicketsQuery" access="public" returntype="any" output="false" roles="" hint="Returns: project">
		<cfargument name="params" default="#structNew()#" />
		<cfset var query = runObjectQuery("project.objectname",arguments.params)>
	  <cfreturn query />
	</cffunction>

	<cffunction name="getTicketHoursQuery" access="public" returntype="any" output="false" roles="" hint="Returns: project">
		<cfargument name="params" default="#structNew()#" />
		<cfset var query = "">
		<cfset arguments.params["totalhours"] = "|isGT|0" />
		<cfset query = runObjectQuery("tickethoursview.objectname",arguments.params)>
	  <cfreturn query />
	</cffunction>

	<cffunction name="getTicketEstimatedHoursQuery" access="public" returntype="any" output="false" roles="" hint="Returns: project">
		<cfargument name="params" default="#structNew()#" />
		<cfset var query = "">
		<cfset arguments.params["estimatedhours"] = "|isLtField|totalhours" />
		<cfset query = runObjectQuery("tickethoursview.objectname",arguments.params)>
	  <cfreturn query />
	</cffunction>

	<cffunction name="getEventsArray" access="public" returntype="any">
		<cfargument name="params" default="#structNew()#" />
	  <cfset var eventparams = structNew() />
	  <cfset var events = arrayNew(1) />
	  <cfset var tickets = structNew() />
	  <cfset var ticketsQuery = "" />
	  <cfset var dtstart = "" />
	  <cfset var dtend = "" />
	  <cfset var curdate = now() />
	  <cfset ticketsQuery = getTicketHoursQuery(arguments.params) />
	  <cfloop query="ticketsQuery">
		  <cfset eventparams = structNew() />
		  <cfif NOT isDate(startdatetime) OR startdatetime eq "">
			  <cfset dtstart = dateAdd("h",-totalhours,ticketmodified) />
			<cfelse>
				<cfset dtstart = startdatetime />
			</cfif>
		  <cfif NOT isDate(enddatetime) OR enddatetime eq "">
			  <cfset dtend = ticketmodified />
			<cfelse>
				<cfset dtend = enddatetime />
			</cfif>
		  <cfset eventparams["dtstart"] = dtstart />
		  <cfset eventparams["dtend"] = dtend />
		  <cfset eventparams["summary"] = "Ticket " & ticketId & ": " & ticketsummary />
		  <cfset eventparams["description"] = ticketdescription />
		  <cfset eventparams["priority"] = 1 />
		  <cfset eventparams["status"] = ticketstatus />
		  <cfset arrayAppend(events,eventparams)>		
		</cfloop>
	  <cfset ticketsQuery = getTicketEstimatedHoursQuery(arguments.params) />
	  <cfset curdate = now() />
	  <cfloop query="ticketsQuery">
		  <cfset esthrs = val(estimatedhours) />
		  <cfif esthrs eq 0>
			  <cfset esthrs = 1>
			</cfif>
		  <cfset eventparams = structNew() />
		  <cfset eventparams["dtstart"] = curdate />
		  <cfset curdate = dateAdd("h",esthrs,curdate) />
		  <cfset eventparams["dtend"] = curdate />
		  <cfset eventparams["summary"] = "Ticket " & ticketId & ": " & ticketsummary />
		  <cfset eventparams["description"] = ticketdescription />
		  <cfset eventparams["status"] = ticketstatus />
		  <cfset eventparams["priority"] = 1 />
		  <cfset arrayAppend(events,eventparams)>
		</cfloop>
		<cfreturn events />
	</cffunction>

	<cffunction name="getTicketById" access="public" returntype="any" output="false" roles="" hint="Returns: project">
		<cfargument name="ticketId" required="true" />
<!---
 		<cfset getBeanInjector().autowire(projectObject)>
 --->
	  <cfreturn getObjectById("ticket.objectname","ticket.primarykey",arguments.ticketId)>
	</cffunction>

	<cffunction name="getTicketBySafeName" access="public" returntype="any" output="false" roles="" hint="Returns: project">
		<cfargument name="projectname" required="true" />
		<cfset var projectObject = createTicketObject()>
		<cfset projectObject.setTicketName(rereplacenocase(arguments.projectname,'[^a-z0-9_]','','all'))>
		<cfset projectObject = getPersistenceService().persisted(projectObject)>
	  <cfreturn projectObject>
	</cffunction>
	
	<cffunction name="getTicketResolutionsQuery" access="public" returntype="any" output="false" roles="" hint="Returns: project">
		<cfargument name="params" default="#structNew()#" />
		<cfset var query = runObjectQuery("ticket.resolution.objectname",arguments.params)>
	  <cfreturn query />
	</cffunction>
	
	<cffunction name="getTicketResolution" access="public" returntype="any" output="false" roles="" hint="Returns: project component">
		<cfargument name="params" default="#structNew()#" />
		<cfset var query = getObject("ticket.resolution.objectname",arguments.params)>
	  <cfreturn query />
	</cffunction>

	<cffunction name="getTicketSeverityQuery" access="public" returntype="any" output="false" roles="" hint="Returns: project">
		<cfargument name="params" default="#structNew()#" />
		<cfset var query = runObjectQuery("ticket.severity.objectname",arguments.params)>
	  <cfreturn query />
	</cffunction>
	
	<cffunction name="getTicketSeverity" access="public" returntype="any" output="false" roles="" hint="Returns: project component">
		<cfargument name="params" default="#structNew()#" />
		<cfset var query = getObject("ticket.severity.objectname",arguments.params)>
	  <cfreturn query />
	</cffunction>

	<cffunction name="getTicketStatusQuery" access="public" returntype="any" output="false" roles="" hint="Returns: project">
		<cfargument name="params" default="#structNew()#" />
		<cfset var query = runObjectQuery("ticket.status.objectname",arguments.params)>
	  <cfreturn query />
	</cffunction>
	
	<cffunction name="getTicketStatus" access="public" returntype="any" output="false" roles="" hint="Returns: project component">
		<cfargument name="params" default="#structNew()#" />
		<cfset var query = getObject("ticket.status.objectname",arguments.params)>
	  <cfreturn query />
	</cffunction>

	<cffunction name="getTicketTypeQuery" access="public" returntype="any" output="false" roles="" hint="Returns: project">
		<cfargument name="params" default="#structNew()#" />
		<cfset var query = runObjectQuery("ticket.type.objectname",arguments.params)>
	  <cfreturn query />
	</cffunction>
	
	<cffunction name="getTicketType" access="public" returntype="any" output="false" roles="" hint="Returns: project component">
		<cfargument name="params" default="#structNew()#" />
		<cfset var query = getObject("ticket.type.objectname",arguments.params)>
	  <cfreturn query />
	</cffunction>

	<cffunction name="getTicketFields" access="public" returntype="any" hint="">
		<cfargument name="params" default="#structNew()#" />
	  <cfset var results = arrayNew(1)>
	  <cfset var myArray = arrayNew(1)>
	  <cfset var subArray = arrayNew(1)>
	  <cfset var summary = structNew()>
	  <cfset var reporter = structNew()>
	  <cfset var owner = structNew()>
	  <cfset var description = structNew()>
	  <cfset var type = structNew()>
	  <cfset var typeoptions = structNew()>
	  <cfset var status = structNew()>
	  <cfset var statusoptions = arraynew(1)>
	  <cfset var priority = structNew()>
	  <cfset var priorityoptions = arraynew(1)>
	  <cfset var milestone = structNew()>
	  <cfset var milestoneoptions = arraynew(1)>
	  <cfset var component = structNew()>
	  <cfset var componentoptions = arraynew(1)>
	  <cfset var version = structNew()>
	  <cfset var versionoptions = arraynew(1)>
	  <cfset var severity = structNew()>
	  <cfset var severityoptions = arraynew(1)>
	  <cfset var resolution= structNew()>
	  <cfset var resolutionoptions = arraynew(1)>
	  <cfset var keywords= structNew()>
	  <cfset var cc= structNew()>
	  <cfset var estimatedhours = structNew()>
	  <cfset var totalhours = structNew()>
	  <cfset var blocking = structNew()>
	  <cfset var blockedby = structNew()>
	  <cfset var priorityorder = structNew()>
	  <cfset var hoursdatetime = structNew()>
	  
	  <cfset summary["type"] = "text">
	  <cfset summary["name"] = "summary">
	  <cfset summary["label"] = "summary">
		<cfset arrayAppend(myArray,summary)>
	  <cfset reporter["type"] = "text">
	  <cfset reporter["name"] = "reporter">
	  <cfset reporter["label"] = "reporter">
		<cfset arrayAppend(myArray,reporter)>
	  <cfset owner["type"] = "text">
	  <cfset owner["name"] = "owner">
	  <cfset owner["label"] = "owner">
		<cfset arrayAppend(myArray,owner)>
	  <cfset description["type"] = "textarea">
	  <cfset description["name"] = "description">
	  <cfset description["label"] = "Description">
		<cfset arrayAppend(myArray,description)>
			<cfset opops = arrayNew(1)>
			<cfset opops[1] = "defect">
			<cfset opops[2] = "enhancement">
			<cfset opops[3] = "task">
	  <cfset type["options"] = opops>
	  <cfset type["type"] = "select">
	  <cfset type["name"] = "type">
	  <cfset type["label"] = "Type">
	  <cfset type["value"] = "defect">
		<cfset arrayAppend(myArray,type)>
	  <cfset status["type"] = "radio">
	  <cfset status["name"] = "status">
	  <cfset status["label"] = "Status">
	  <cfset status["value"] = "">
	  <cfset status["optional"] = "$boolean1">
		<cfset statusoptions[1] = "new">
		<cfset statusoptions[2] = "assigned">
		<cfset statusoptions[3] = "reopened">
		<cfset statusoptions[4] = "closed">
	  <cfset status["options"] = statusoptions>
		<cfset arrayAppend(myArray,status)>

	  <cfset priority["type"] = "select">
	  <cfset priority["name"] = "priority">
	  <cfset priority["label"] = "priority">
	  <cfset priority["value"] = "normal">
	  <cfset priority["optional"] = "$boolean1">
		<cfset priorityoptions[1] = "highest">
		<cfset priorityoptions[2] = "high">
		<cfset priorityoptions[3] = "normal">
		<cfset priorityoptions[4] = "low">
		<cfset priorityoptions[4] = "lowest">
	  <cfset priority["options"] = priorityoptions>
		<cfset arrayAppend(myArray,priority)>

	  <cfset milestone["type"] = "select">
	  <cfset milestone["name"] = "milestone">
	  <cfset milestone["label"] = "milestone">
	  <cfset milestone["value"] = "">
	  <cfset milestone["optional"] = "$boolean1">
		<cfset milestoneoptions[1] = "milestone1">
		<cfset milestoneoptions[2] = "milestone2">
		<cfset milestoneoptions[3] = "milestone3">
		<cfset milestoneoptions[4] = "milestone4">
	  <cfset milestone["options"] = milestoneoptions>
		<cfset arrayAppend(myArray,milestone)>

	  <cfset component["type"] = "select">
	  <cfset component["name"] = "component">
	  <cfset component["label"] = "component">
	  <cfset component["value"] = "component1">
	  <cfset component["optional"] = "$boolean1">
		<cfset componentoptions[1] = "component1">
		<cfset componentoptions[2] = "component2">
	  <cfset component["options"] = componentoptions>
		<cfset arrayAppend(myArray,component)>

	  <cfset version["type"] = "select">
	  <cfset version["name"] = "version">
	  <cfset version["label"] = "version">
	  <cfset version["value"] = "version1">
	  <cfset version["optional"] = "$boolean1">
		<cfset versionoptions[1] = "$string2.0">
		<cfset versionoptions[2] = "$string1.0">
	  <cfset version["options"] = versionoptions>
		<cfset arrayAppend(myArray,version)>

	  <cfset severity["type"] = "select">
	  <cfset severity["name"] = "severity">
	  <cfset severity["label"] = "severity">
	  <cfset severity["value"] = "normal">
	  <cfset severity["defaultvalue"] = "normal">
	  <cfset severity["optional"] = "$boolean1">
		<cfset severityoptions[1] = "blocker">
		<cfset severityoptions[2] = "critical">
		<cfset severityoptions[3] = "major">
		<cfset severityoptions[4] = "normal">
		<cfset severityoptions[5] = "minor">
		<cfset severityoptions[6] = "trivial">
	  <cfset severity["options"] = severityoptions>
		<cfset arrayAppend(myArray,severity)>

	  <cfset resolution["type"] = "radio">
	  <cfset resolution["name"] = "resolution">
	  <cfset resolution["label"] = "resolution">
	  <cfset resolution["value"] = "">
	  <cfset resolution["optional"] = "$boolean1">
		<cfset resolutionoptions[1] = "fixed">
		<cfset resolutionoptions[2] = "invalid">
		<cfset resolutionoptions[3] = "wontfix">
		<cfset resolutionoptions[4] = "duplicate">
		<cfset resolutionoptions[5] = "worksforme">
	  <cfset resolution["options"] = resolutionoptions>
		<cfset arrayAppend(myArray,resolution)>

	  <cfset keywords["type"] = "text">
	  <cfset keywords["name"] = "keywords">
	  <cfset keywords["label"] = "keywords">
		<cfset arrayAppend(myArray,keywords)>

	  <cfset cc["type"] = "text">
	  <cfset cc["name"] = "cc">
	  <cfset cc["label"] = "cc">
		<cfset arrayAppend(myArray,cc)>

	  <cfset priorityorder["type"] = "text">
	  <cfset priorityorder["name"] = "priorityorder">
	  <cfset priorityorder["label"] = "Priority Order">
	  <cfset priorityorder["value"] = "$string0">
	  <cfset priorityorder["custom"] = "$boolean1">
	  <cfset priorityorder["order"] = "$int1">
		<cfset arrayAppend(myArray,priorityorder)>

	  <cfset estimatedhours["name"] = "estimatedhours">
	  <cfset estimatedhours["value"] = "$string0">
	  <cfset estimatedhours["optional"] = "$boolean0">
	  <cfset estimatedhours["custom"] = "$boolean1">
	  <cfset estimatedhours["label"] = "Estimated Hours">
	  <cfset estimatedhours["type"] = "text">
	  <cfset estimatedhours["order"] = "$int2">
		<cfset arrayAppend(myArray,estimatedhours)>

	  <cfset totalhours["type"] = "text">
	  <cfset totalhours["name"] = "totalhours">
	  <cfset totalhours["label"] = "Actual Hours">
	  <cfset totalhours["value"] = "$string0">
	  <cfset totalhours["order"] = "$int3">
	  <cfset totalhours["width"] = "$int3">
	  <cfset totalhours["optional"] = "$boolean1">
	  <cfset totalhours["custom"] = "$boolean1">
		<cfset arrayAppend(myArray,totalhours)>

	  <cfset addhours["type"] = "text">
	  <cfset addhours["name"] = "addhours">
	  <cfset addhours["label"] = "Add Hours">
	  <cfset addhours["value"] = "$string0">
	  <cfset addhours["order"] = "$int3">
	  <cfset addhours["width"] = "$int3">
	  <cfset addhours["optional"] = "$boolean1">
	  <cfset addhours["custom"] = "$boolean1">
		<cfset arrayAppend(myArray,addhours)>

	  <cfset hoursdatetime["type"] = "text">
	  <cfset hoursdatetime["name"] = "hoursdatetime">
	  <cfset hoursdatetime["label"] = "Hours DateTime">
	  <cfset hoursdatetime["order"] = "$int4">
	  <cfset hoursdatetime["width"] = "$int10">
	  <cfset hoursdatetime["optional"] = "$boolean1">
	  <cfset hoursdatetime["custom"] = "$boolean1">
		<cfset arrayAppend(myArray,hoursdatetime)>

	  <cfset blocking["type"] = "text">
	  <cfset blocking["name"] = "blocking">
	  <cfset blocking["label"] = "Blocking">
	  <cfset blocking["order"] = "$int5">
	  <cfset blocking["width"] = "$int10">
	  <cfset blocking["optional"] = "$boolean1">
	  <cfset blocking["custom"] = "$boolean1">
		<cfset arrayAppend(myArray,blocking)>

	  <cfset blockedby["type"] = "text">
	  <cfset blockedby["name"] = "blockedby">
	  <cfset blockedby["label"] = "Blocked By">
	  <cfset blockedby["order"] = "$int6">
	  <cfset blockedby["width"] = "$int10">
	  <cfset blockedby["optional"] = "$boolean1">
	  <cfset blockedby["custom"] = "$boolean1">
		<cfset arrayAppend(myArray,blockedby)>


    <cfreturn myArray />
	</cffunction>

	<cffunction name="getTicketQuery" access="public" returntype="any" output="false" roles="" hint="Returns: project">
		<cfargument name="params" default="#structNew()#" />
		<cfset var query = runObjectQuery("ticket.objectname",arguments.params)>
	  <cfreturn query />
	</cffunction>

	<cffunction name="getTicket" access="public" returntype="any" output="false" roles="" hint="Returns: project component">
		<cfargument name="params" default="#structNew()#" />
		<cfset var ticket = getObject("ticket.objectname",arguments.params) />
	  <cfreturn ticket />
	</cffunction>

	<cffunction name="getTicketView" access="public" returntype="any" output="false" roles="" hint="Returns: project component">
		<cfargument name="params" default="#structNew()#" />
		<cfset var ticket = getObject("ticketview.objectname",arguments.params) />
	  <cfreturn ticket />
	</cffunction>

	<cffunction name="notifyTicket" access="public" returntype="any" output="false" roles="" hint="Returns: project component">
		<cfargument name="ticket" required="true" />
		<cfset var tkt = arguments.ticket />
		<cfset var svc = getEmailService() />
		<cfset var email = svc.createEmail() />
		<cfset var toList = "" />
		<cfset toList = tkt.getOwner().getEmail() >
		<cfset email.setTo(toList) />
		<cfset email.setSubject("Test") />
		<cfset email.setBody("Test") />
		<cfset email.send() />
	</cffunction>

	<cffunction name="persistTicketComment" access="public" returntype="any" output="false" roles="" hint="Returns: project component">
		<cfargument name="ticket" required="true" />
		<cfargument name="comment" required="true" />
		<cfargument name="userId" required="true" />
		<cfscript>
			var ticketRecord = arguments.ticket;
			var ticketComment = "";
			ticketComment = ticketRecord.getCommentIterator().add();
			ticketComment.setTicketId(ticketRecord.getticketId());
			ticketComment.setCommentText(arguments.comment);
			ticketComment.setCreateUserId(arguments.userId);
			ticketComment.setCreateDate(now());
			ticketComment.save();				
		</cfscript>
	</cffunction>

	<cffunction name="throwError" access="private" returntype="any" output="false" roles="" hint="Returns: project component">
		<cfargument name="errorType" required="true" />
		<cfargument name="errorMessage" required="true" />
		<cfthrow type="#arguments.errorType#" message="#arguments.errorMessage#" />
	</cffunction>

	<cffunction name="setAttribute" access="private" returntype="any" output="false" roles="" hint="Returns: project component">
		<cfargument name="ticket" required="true" />
		<cfargument name="attribute" required="true" />
		<cfargument name="value" required="true" />
		<cfinvoke component="#arguments.ticket#" method="setTicket#arguments.attribute#" >
				<cfinvokeargument name="ticket#arguments.attribute#" value="#arguments.value#" />
		</cfinvoke>
	</cffunction>

	<cffunction name="getAttribute" access="private" returntype="any" output="false" roles="" hint="Returns: project component">
		<cfargument name="ticket" required="true" />
		<cfargument name="attribute" required="true" />
		<cfset var retVal = "">
		<cfinvoke component="#arguments.ticket#" method="getTicket#arguments.attribute#" returnvariable="retVal" />
		<cfreturn retVal />
	</cffunction>

	<cffunction name="persistTicket" access="public" returntype="any" output="false" roles="" hint="Returns: project component">
		<cfargument name="params" default="#structNew()#" />
		<cfscript>
			var ticketRecord = "";
			var ticket = structNew();
			var ticketAttributes = params["ticketAttributes"];
			var userId = params["userId"];
			var projectId = params["projectId"];
			var attrib = "";
			var attribList = "summary,cc,component,description,keywords,milestone,priority,severity,version,status,resolution,type,blocking,blockedby";
			var totalhours = 0;
			var loc = structNew();
			searchRecord = getObject("ticket.objectname",ticket);
			ticket.ticketId = params["ticketId"];
			ticketRecord = getObject("ticket.objectname",ticket);
			ticket.ticketNotifys = params["ticketNotifys"];
			if(structKeyExists(ticketAttributes,"comment") AND ticketAttributes["comment"] gt "") {
				persistTicketComment(ticketRecord,ticketAttributes["comment"],userId);
			}
			for(x =1; x lte listLen(attribList); x = x+1) {
				attrib = listGetAt(attribList,x);
				if(structKeyExists(ticketAttributes,attrib) AND ticketAttributes[attrib] gt "") {
					if(val(ticket.ticketId) neq 0) {
						oldAttribVal = getAttribute(ticketRecord,attrib);
						if(oldAttribVal neq ticketAttributes[attrib]) {
							if(oldAttribVal gt "") {
								oldAttribVal = " from " & oldAttribVal;
							}
							persistTicketComment(ticketRecord, attrib & " changed" & oldAttribVal & " to " & ticketAttributes[attrib], userId);
						}
					}
					setAttribute(ticketRecord,attrib,ticketAttributes[attrib]);
				}
			}
			if(structKeyExists(ticketAttributes,"owner") AND ticketAttributes.owner gt "") {
				if(not isNumeric(ticketAttributes.owner)) {
				  loc.ownername = ticketAttributes.owner;
				  loc.owner = searchRecord.getOwner().load(username = ticketAttributes.owner);
				  ticketAttributes.owner = loc.owner.getUserId();
				  if(ticketAttributes.owner eq 0) {
				  	throwError("TicketService.saveticket.error","Owner: No user by the name of: " & loc.ownername);
				  }
				}
				ticketRecord.setTicketOwner(ticketAttributes.owner);
			}
			if(structKeyExists(ticketAttributes,"reporter")) {
				if(not isNumeric(ticketAttributes.reporter)) {
				  loc.reportername = ticketAttributes.reporter;
				  loc.reporter = searchRecord.getReporter().load(username = ticketAttributes.reporter);
				  ticketAttributes.reporter = loc.reporter.getUserId();
				  if(ticketAttributes.reporter eq 0) {
				  	throwError("TicketService.saveticket.error","Reporter: No user by the name of: " & loc.reportername);
				  }
				}
				ticketRecord.setTicketReporter(ticketAttributes.reporter);
			}
			if(structKeyExists(ticketAttributes,"estimatedhours")) {
				ticketRecord.setEstimatedHours(ticketAttributes.estimatedhours);
			}
			if(structKeyExists(ticketAttributes,"blocking")) {
				ticketRecord.setTicketBlocking(ticketAttributes.blocking);
			}
			if(structKeyExists(ticketAttributes,"blockedby")) {
				ticketRecord.setTicketBlockedby(ticketAttributes.blockedby);
			}
			if(structKeyExists(ticketAttributes,"totalhours")) {
				//ticketRecord.settotalhours(ticketAttributes.totalhours);
			}
			if(structKeyExists(ticketAttributes,"addhours") AND val(ticketAttributes.addhours) neq 0) {
				totalhours = val(ticketRecord.getTotalHours()) + val(ticketAttributes.addhours);
				ticketRecord.setTotalHours(totalhours);
				tickethours = ticketRecord.getTicketHoursIterator().add();
				tickethours.setNumHours(ticketAttributes.addhours);
				hoursdatetime = ticketAttributes.hoursdatetime;
				if(NOT isDate(hoursdatetime)) { 
					hoursdatetime = now(); 
				}
				startdatetime = dateAdd("h",-ticketAttributes.addhours,hoursdatetime);
				tickethours.setStartDateTime(startdatetime);
				tickethours.setEndDateTime(hoursdatetime);
				tickethours.setCreateUserId(userId);
				tickethours.setCreateDate(now());
				tickethours.setModifiedUserId(userId);
				tickethours.setModifiedDate(now());
			}
			if(structKeyExists(ticketAttributes,"ticketpriorityorder")) {
				ticketRecord.setTicketPriorityOrder(ticketAttributes.ticketpriorityorder);
			}
			if(ticket.ticketId eq 0) {
				ticketRecord.setTicketCreated(dateAdd("h",7,now())); // TODO: handle timezone (UTC -7) 
				ticketRecord.setCreateUserId(userId);
				ticketRecord.setTicketReporter(userId);
				ticketRecord.setProjectId(projectId);
				ticketRecord.setTicketStatus("new");
			}
			ticketRecord.setTicketModified(dateAdd("h",7,now()));  // TODO: handle timezone (UTC -7)
			ticketRecord.setModifiedUserId(userId);
			ticketRecord.save();
			if (ticket.ticketNotifys) {
				notifyTicket(ticketRecord);
			}
			return ticketRecord;
		</cfscript>
	</cffunction>

	<cffunction name="saveAttachment" access="public" returntype="any" hint="">
		<cfargument name="params">
		<cfscript>
			var attachment = structNew();
			var attachmentRecord = "";
			attachment["ticketId"] = params["ticketId"];
			attachment["AttachmentFileName"] = params["AttachmentFileName"];
			// load the record, if there is one, using ticketId and filename as keys
			attachmentRecord = getObject("ticket.attachment.objectname",attachment);
			attachmentRecord.setTicketId(arguments.params["ticketId"]);
			attachmentRecord.setAttachmentFileName(arguments.params["AttachmentFileName"]);
			attachmentRecord.setAttachmentDescription(arguments.params["AttachmentDescription"]);
			attachmentRecord.setAttachmentFileData(toBase64(arguments.params["AttachmentFileData"]));
			attachmentRecord.setAttachmentFileSize(len(arguments.params["AttachmentFileData"]));
			attachmentRecord.setCreateDate(now());
			attachmentRecord.setCreateUserId(arguments.params["CreateUserId"]);
			attachmentRecord.setModifiedDate(now());
			attachmentRecord.getTicket().setTicketModified(now());
			ticketComment = attachmentRecord.getTicket().getCommentIterator().add();
			ticketComment.setTicketId(arguments.params["ticketId"]);
			ticketComment.setCommentText("added attachment: #arguments.params["AttachmentFileName"]#");
			ticketComment.setCreateUserId(arguments.params["CreateUserId"]); // TODO: get user id?
			ticketComment.setCreateDate(now());
			ticketComment.save();				

			//TODO: overwrite = arguments.params[4];
			attachmentRecord.save();
			return attachmentRecord;
		</cfscript>
	</cffunction>

	<cffunction name="viewAttachment" access="public" returntype="any" hint="">
		<cfargument name="params">
		<cfscript>
			var attachment = structNew();
			var attachmentRecord = "";
			var cvalue = "";
			attachment["ticketId"] = params[1];
			attachment["AttachmentFileName"] = params[2];
			attachmentRecord = getObject("ticket.attachment.objectname",attachment);
		</cfscript>
		<cfif isDefined('url.d')>
		 <cfset cvalue = 'attachment;'>
		</cfif>
		<cfset cvalue = cvalue & "filename=""#attachmentRecord.getAttachmentFileName()#""">
		<cfoutput><cfcontent type="text/plain" reset="true"><cfheader name="Content-Disposition" value="#cvalue#">#toString(toBinary(attachmentRecord.getAttachmentFileData()))#</cfoutput><cfabort>
	</cffunction>

	<cffunction name="getAttachment" access="public" returntype="any" hint="">
		<cfargument name="params">
		<cfscript>
			var attachmentRecord = "";
			var cvalue = "";
			attachmentRecord = getObject("ticket.attachment.objectname",arguments.params);
			return attachmentRecord;
		</cfscript>
	</cffunction>


	<cffunction name="getRecentChangesQuery" access="public" returntype="any" output="false" roles="" hint="Returns: project">
		<cfargument name="projectId" required="true" type="numeric">
		<cfargument name="params" default="#structNew()#" />
		<cfset var qryParams = structNew() />
		<cfset var query = "" />
		<cfset qryParams["projectId"] = arguments.projectId />
		<cfset qryParams["ticketModified"] = "|isGTE|" & params["ticketModified"] />
		<cfset query = runObjectQuery("ticket.objectname",qryParams) />
	  <cfreturn query />
	</cffunction>
	

	<cffunction name="getTicketCommentsQuery" access="public" returntype="any" output="false" roles="" hint="Returns: project">
		<cfargument name="params" default="#structNew()#" />
		<cfset var query = runObjectQuery("ticket.comment.objectname",arguments.params)>
	  <cfreturn query />
	</cffunction>

	<cffunction name="getTicketAttachmentsQuery" access="public" returntype="any" output="false" roles="" hint="Returns: project">
		<cfargument name="params" default="#structNew()#" />
		<cfset var query = runObjectQuery("ticket.attachment.objectname",arguments.params)>
	  <cfreturn query />
	</cffunction>

	<cffunction name="getTicketActionQuery" access="public" returntype="any" output="false" roles="" hint="Returns: project">
		<cfargument name="params" default="#structNew()#" />
		<cfset var query = runObjectQuery("ticket.action.objectname",arguments.params)>
	  <cfreturn query />
	</cffunction>

	<cffunction name="getTicketAvailableActionsQuery" access="public" returntype="any" output="false" roles="" hint="Returns: project">
		<cfargument name="params" default="#structNew()#" />
		<cfset var ticket = getTicketById(params["ticketId"]) />
		<cfset var prams = structNew()>
		<cfset var query = "">
		<cfset prams["projectId"] = ticket.getProjectId() />
		<cfset query = runObjectQuery("ticket.action.objectname",prams) />
		<cfif ticket.getTicketStatus() eq "closed">
		  <cfquery name="query" dbtype="query">
			  SELECT *
			  FROM query WHERE actionname like 'reopen' OR actionname like 'leave'
			</cfquery>
		</cfif>		
	  <cfreturn query />
	</cffunction>

	<cffunction name="getTicketAction" access="public" returntype="any" output="false" roles="" hint="Returns: project component">
		<cfargument name="params" default="#structNew()#" />
		<cfset var query = getObject("ticket.action.objectname",arguments.params)>
	  <cfreturn query />
	</cffunction>

</cfcomponent>